package org.esk.dablog.service.spi;

import org.esk.dablog.service.EntryManager;
import org.esk.dablog.dao.EntryDAO;
import org.esk.dablog.dao.CommentDAO;
import org.esk.dablog.model.Entry;
import org.esk.dablog.model.Picture;
import org.esk.dablog.model.Author;
import org.esk.dablog.model.Comment;
import org.esk.dablog.model.forum.ForumPostEntry;
import org.esk.dablog.model.comments.RideJoinComment;
import org.esk.dablog.model.entries.Ride;
import org.esk.dablog.ApplicationConstants;
import org.esk.dablog.exceptions.BusinessException;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.List;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import java.io.IOException;

/**
 * This class
 * User: jc
 * Date: 22.11.2006
 * Time: 12:37:11
 * $Id:$
 */
public class EntryManagerImpl implements EntryManager {
    private static final Log logger = LogFactory.getLog(EntryManagerImpl.class);

    private EntryDAO entryDAO;
    private CommentDAO commentDAO;
    private ThumbnailsHelper thumbHelper;

    public void setEntryDAO(EntryDAO entryDAO) {
        this.entryDAO = entryDAO;
    }


    public void setThumbHelper(ThumbnailsHelper thumbHelper) {
        this.thumbHelper = thumbHelper;
    }


    public void setCommentDAO(CommentDAO commentDAO) {
        this.commentDAO = commentDAO;
    }

    /**
     * adds new entry
     *
     * @param e
     * @return
     */
    public long addEntry(Entry e) throws BusinessException {
        if (e.getAuthor() == null) {
            throw new BusinessException("Cannot add entry without author");
        }
        if (e.getDate() == null) {
            e.setDate(new Date());
        }
        return entryDAO.addEntry(e);
    }


    /**
     * lists all entries
     *
     * @return
     */
    public List<Entry> listEntries() {
        return entryDAO.listEntries(Entry.class, 0);
    }

    /**
     * lists count entries
     *
     * @param count
     * @return
     */
    public List<Entry> listEntries(Class <? extends Entry>clazz, int count) {
        return entryDAO.listEntries(clazz, count);
    }


    /**
     * returns count of entries for each day in month
     *
     * @param c
     * @return
     */
    public List<Integer> listMonthCalendar(Class <? extends Entry> clazz, Calendar c) {
        Calendar start = new GregorianCalendar(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
                c.getActualMinimum(Calendar.DAY_OF_MONTH));
        Calendar end = new GregorianCalendar(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
                c.getActualMaximum(Calendar.DAY_OF_MONTH));
        List<Date> dates = entryDAO.listEntriesPerDate(clazz, start.getTime(), end.getTime());
        Map<Date, Integer> calendar = new HashMap<Date, Integer>();
        for (Date d : dates) {
            Date td = DateUtils.truncate(d, Calendar.DAY_OF_MONTH);
            int count = calendar.containsKey(td) ? (Integer) calendar.get(td) : 0;
            calendar.put(td, ++count);
        }
        Calendar current = (Calendar) start.clone();
        List<Integer> result = new ArrayList<Integer>();
        for (int i = start.get(Calendar.DAY_OF_MONTH); i <= end.get(Calendar.DAY_OF_MONTH); ++i) {
            current.set(Calendar.DAY_OF_MONTH, i);
            Date td = DateUtils.truncate(current.getTime(), Calendar.DAY_OF_MONTH);
            int count = calendar.containsKey(td) ? (Integer) calendar.get(td) : 0;
            result.add(count);
        }
        return result;
    }

    /**
     * @param d - date
     * @return
     */
    public List listEntriesByDate(Class<? extends Entry> clazz, Date d) {
        Calendar start = Calendar.getInstance();
        start.setTime(d);
        DateUtils.truncate(start, Calendar.DAY_OF_MONTH);
        Calendar end = (Calendar) start.clone();
        end.add(Calendar.DAY_OF_MONTH, 1);
        end.add(Calendar.SECOND, -1);

        return entryDAO.listEntriesByDate(clazz, start.getTime(), end.getTime());
    }

    /**
     * @param entryId
     * @return
     */
    public Entry getEntry(Class <? extends Entry> clazz, long entryId) throws BusinessException {
        Entry e = entryDAO.getEntry(clazz, entryId);
        if (e == null) {
            throw new BusinessException("Entry with id " + entryId + " not found");
        }
        return e;
    }

    /**
     * @param entry
     */
    public synchronized void updateEntry(Entry entry) {
        String imagePath = ApplicationConstants.getInstance().getPathToImages();
        try {
            FileUtils.forceMkdir(new File(imagePath));
        } catch (IOException e) {
            //TODO Convert to BusinessException
            throw new RuntimeException("Error creating directory " + imagePath);
        }
        if (entry.getPictures() != null) {
            for (Picture p : entry.getPictures()) {
                if (p.getData() == null || ArrayUtils.isEmpty(p.getData())) {
                    //do not process pictures without data
                    continue;
                }
                if (p.getAuthor() == null) {
                    p.setAuthor(entry.getAuthor());
                }
                File f = null;
                File thumb = null;
                String newFilename = null;
                while (f == null || f.exists()) {
                    newFilename = RandomStringUtils.randomAlphanumeric(10);
                    f = new File(imagePath + "/" + newFilename + p.getOriginalFilename());
                }
                thumb = new File(imagePath + "/thumb_" + newFilename + p.getOriginalFilename());
                try {
                    FileUtils.writeByteArrayToFile(f, p.getData());
                    FileUtils.writeByteArrayToFile(thumb, thumbHelper.createThumbnail(p.getData()));
                } catch (Exception ex) {
                    logger.error("Error saving uploaded file: ", ex);
                }
                p.setLocalPath(f.getName());
                p.setThumbnailPath(thumb.getName());
            }
        }
        entryDAO.updateEntry(entry);
    }

    /**
     * adds author tothe ride
     *
     * @param ride
     * @param author
     */
    public void addAuthorToRide(Ride ride, Author author) {
        if (ride.isOnRide(author.getUsername())) {
            //author is already on ride
            //TODO implement "already on ride" error message
            return;
        }
        ride.join(author);
        updateEntry(ride);
    }

    /**
     * removes author from the ride
     *
     * @param ride
     * @param author
     */
    public void removeAuthorFromRide(Ride ride, Author author) {
        if (ride == null || author == null) {
            return;
        }
        if (ride.isOnRide(author.getUsername())) {
            ride.split(author);
            updateEntry(ride);
        }
    }

    /**
     *
     * @param clazz
     * @return
     */
    public List listEntries(Class<? extends Entry> clazz) {
        return entryDAO.listEntries(clazz);
    }

    /**
     *
     * @param entryClasses
     * @param start
     * @param count
     * @return
     */
    public List<Entry> listEntries(List<Class<? extends Entry>> entryClasses, int start, int count) {
        return entryDAO.listEntries(entryClasses, start, count);
    }

    /**
     *
     * @param c
     */
    public void saveComment(Comment c) {
        commentDAO.saveComment(c);
    }

    public Comment getComment(long commentId) {
        return commentDAO.getComment(commentId);
    }

    public List<Comment> listComments(Entry entry, int start, int count) {
        return commentDAO.listComments(entry, start,count);
    }

    public List listVisibleEntries(Class<? extends Entry> aClass, int i) {
        return entryDAO.listVisibleEntries(aClass, i);
    }

    /**
     * deletes entry
     * @param entryId
     */
    public void deleteEntry(long entryId) {
        entryDAO.deleteEntry(entryId);
    }


}
